map_cleanup: phantom orientations — picker scores height, not prominence - #97
Merged
Conversation
The angle labels were drawn with their baseline at y=22 under the 18 px title bar that _label() paints over the finished panel, hiding their top halves; the right-edge reserve was also too narrow for three digits. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013o39JoxarofwaPi6kGYxXY
…e zero The declutter pass kept any local maximum of the angular energy reaching 0.45 of the global maximum. Clutter puts energy at every orientation, so a real map's curve is a few wall peaks riding on a pedestal measuring ~0.49 of that maximum: more than half the threshold's range sits below the pedestal, where everything clears the gate and what survives is whatever the 12 deg suppression radius leaves. On the 2026-08-02 flat map that was a phantom at 73.8 deg -- the shoulder of the real 86.8 deg family, 13.0 deg away -- reaching 0.50 against the weakest real family's 0.64. Six of the thirteen real occupancy maps on the box carried such a shoulder, every one 12.0-13.5 deg from a stronger peak. Threshold the floor-subtracted residual instead (the same curve and the same 45 deg floor angular_stats already used, at the same 0.15): those two peaks read 0.012 and 0.296, 24x apart rather than 1.3x. Both halves have to move together, and a test pins that -- 0.45 on the residual drops real families, 0.15 on the raw curve returns four directions on a clean rectilinear map, two of them flank samples. Literal topographic prominence was measured first and is worse: the real off-axis family scores 0.033 against the phantom's 0.028, since it sits on the dominant family's tail and is a shoulder in the same sense; and thresholding prominence promotes isolated bumps in the noise floor that carry no structural energy at all. A wider suppression radius cannot separate them either (13.0 deg to its parent against the real family's 14.5 deg). Over every genuine occupancy map on the box: 7 unchanged including the whole tuning set and all three sim worlds, 6 lost exactly one shoulder each, none gained a direction. The flat map now cleans byte-identically to the --peak-rel 0.55 output the operator threaded by hand. Room segmentation, which picks through the same function, is unchanged on the sim ladder at 0, +17 and -31 deg. The diagnostics panel now plots both curves with the threshold across them, because one showing only the raw energy cannot explain a rejection. Measurements in docs/tuning/2026-08-11-orientation-picking.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U5jicDV5ybEtZfvtwrqWNu
The module cites ROSE, so it should say where it departs from it. ROSE selects directions by topographic prominence at 50% of the angular curve's peak-to-trough range (arXiv:2004.08794 3.1; literally find_peaks(pol_h, prominence=(max-min)*0.5) in aislabunimi/ROSE2). The shipped relative-height rule was never that, and neither is the floor-subtracted one replacing it. Run over the same 13 maps, ROSE's criterion is more conservative than either: exactly the two strongest near-orthogonal directions everywhere, never a phantom -- and never the real off-axis families this pass has to keep, which is the two-direction result rejected at --peak-rel 0.65 for visibly eroding walls. That is a difference in the maps, not a mistake in the paper: ROSE scores large rectilinear floor plans and abstracts one, where this gates observed pixels to real walls on a small flat that has genuine off-axis families. Corrects the earlier claim that prominence is simply worse. It is not worse at the paper's threshold -- it just has no setting that keeps the third and fourth families while excluding a shoulder, because the real off-axis family's prominence (0.033 of the maximum) sits below the phantom's neighbours and beside the phantom's own 0.028. No behaviour change: docstrings, README, tuning note and the rose-rule.txt measurement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U5jicDV5ybEtZfvtwrqWNu
Both branches added a 2026-08-11 tuning note to the mkdocs nav; keep both, in filename order. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U5jicDV5ybEtZfvtwrqWNu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
map_cleanup: pick wall directions above the broadband floor, not above zero.
Branch
map-cleanup-peak-prominence, 3 commits on main: the cherry-picked diagnostics label fix frommap-cleanup-diag-labels, the picker change, and a docs-only commit recording how the picker relates to ROSE.What was wrong
_pick_directionskept any local maximum of the angular energy reaching 0.45 of the global maximum. Clutter puts energy at every orientation, so a real map's curve is a few wall peaks riding on a pedestal measuring ~0.49 of that maximum -- more than half the threshold's range sits below the pedestal, everything clears the gate, and what survives is whatever the 12 deg suppression radius leaves. On the 2026-08-02 flat map that was the 73.8 deg phantom: the shoulder of the real 86.8 deg family, 13.0 deg away, at 0.50 of the max against the weakest real family's 0.64 (1.3x apart). Six of the thirteen real occupancy maps on the box carried such a shoulder, every one 12.0-13.5 deg from a stronger peak.The flat map's angular energy. Top: the shipped rule, with 74 deg picked off the flank of the real 87 deg family. Bottom: the same curve (dim) with the floor-subtracted residual over it and the threshold across — where the phantom is a flat nothing. Full panel and data.
The change
Threshold the floor-subtracted residual -- the same 45 deg floor
angular_statsalready subtracted, at the same 0.15. Those two peaks then read 0.012 and 0.296, 24x apart._pick_peaksis the NMS loop;_pick_directions= floor-subtract +_pick_peaks;_floor_subtractis shared with_residual_spectrum.angular_statscalls_pick_peakson its already-residual curve, so its behaviour is bit-identical.Params/SpectrumParams.peak_rel_threshold0.45 -> 0.15. Both halves must travel together, and a test pins that.room_segmentation.dominant_rotation_degnow ranks on the same residual it picked from.Relation to ROSE (the third commit)
The module cites ROSE, so it should say where it departs. ROSE selects directions by topographic prominence at 50% of the curve's peak-to-trough range (arXiv:2004.08794 3.1; literally
find_peaks(pol_h, prominence=(max-min)*0.5)in aislabunimi/ROSE2). The shipped relative-height rule was never that, and neither is this one -- so the phantom came from a pre-existing deviation from the paper, not from the paper.Run over the same 13 maps, ROSE's criterion is more conservative than either: exactly the two strongest near-orthogonal directions everywhere, never a phantom -- and never the real off-axis families this pass has to keep (11.8 / 110.2 deg on the flat), which is the two-direction result rejected at
--peak-rel 0.65for visibly eroding walls. That is a difference in the maps rather than a mistake in the paper: ROSE scores large rectilinear floor plans and abstracts one, where this gates observed pixels to real walls on a small flat that has genuine off-axis families.So prominence is not simply worse -- it has no setting that expresses what is wanted here. The paper's threshold gives two directions; any threshold loose enough to admit the third and fourth admits the shoulder with them, the real off-axis family scoring 0.033 of the maximum against the phantom's 0.028. Looser still and it starts promoting lone bumps in the noise floor with no structural energy at all (47.2 deg on the tuning map, 43.2 / 137.2 deg on the replay maps). A wider suppression radius cannot separate them either: 13.0 deg to the phantom's parent against the real family's 14.5 deg.
Verification
map_raw_notraj.png;bag_replay_results/*/*/map.pngis a rendered figure, not a map, and was excluded): 7 unchanged -- including the whole tuning set and all three sim worlds -- 6 lost exactly one shoulder each (12.0-13.0 deg from a stronger peak, residual 0.012-0.105 against a weakest-kept 0.204-0.285), none gained a direction.--peak-rel 0.55output threaded by hand on 2026-08-02 (0 differing cells of 43 259); the old default differed by 664.sim/mote_world(residual 0.109), 0.20 drops a real family on the tuning map (0.189).segment-evalunchanged at 0 / +17 / -31 deg (30/33 hospital, 10/10 office, 1/1 mote, zero merges) -- identical to a baseline run of unpatched main.test_angular_stats,test_room_segmentation,test_sites,test_bundle), including 3 new picker tests; pre-commit clean across the tree.Measurements and data in
docs/tuning/2026-08-11-orientation-picking.md(+ dir: corpus, threshold sweep, ROSE-rule comparison, before/after figure), registered in the mkdocs nav.Follow-up filed as task 380: the CLI, the diagnostics labels and every saved revision's
meta.yamlcall these "orientations" when they are wall normals, 90 deg from whatangular_statsreports for the same map.